﻿
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading
Imports osi.root.constants
Imports osi.root.connector
Imports osi.root.procedure
Imports osi.root.formation
Imports osi.root.utils
Imports osi.root.lock
Imports osi.root.envs
Imports osi.service.dns

'create connections for a powerpoint
Public Class connector
    Private Shared Function verify_token(ByVal p As powerpoint, ByVal c As TcpClient) As event_comb
        assert(Not p Is Nothing)
        assert(Not c Is Nothing)
        Dim ec As event_comb = Nothing
        Dim rec As pointer(Of Byte()) = Nothing
        Return New event_comb(Function() As Boolean
                                  If String.IsNullOrEmpty(p.token) Then
                                      Return goto_end()
                                  Else
                                      rec = New pointer(Of Byte())()
                                      ec = c.question(p,
                                                      str_bytes(p.token),
                                                      rec)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  End If
                              End Function,
                              Function() As Boolean
                                  If Not ec.end_result() OrElse
                                     isemptyarray(+rec) OrElse
                                     Not strsame(bytes_str(+rec), p.token) Then
                                      If tcp_trace Then
                                          raise_error(p.identity(),
                                                      " actively reject the token ",
                                                      p.token,
                                                      ", response is ",
                                                      bytes_str(+rec),
                                                      ", will close the connection")
                                      End If
                                      Return False
                                  Else
                                      Return goto_end()
                                  End If
                              End Function)
    End Function

    Private Shared Function generate_connection(ByVal add As IPAddress, ByVal p As powerpoint) As event_comb
        assert(Not p Is Nothing)
        assert(p.is_outgoing)
        Dim ec As event_comb = Nothing
        Dim c As TcpClient = Nothing
        Return New event_comb(Function() As Boolean
                                  assert(Not add Is Nothing)
                                  c = New TcpClient(If(p.ipv4,
                                                       AddressFamily.InterNetwork,
                                                       AddressFamily.InterNetworkV6))
                                  c.set_no_delay(True)
                                  ec = _tcpclient.connect(c, New IPEndPoint(add, p.port))
                                  Return waitfor(ec, p.connecting_timeout_ms) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      ec = verify_token(p, c)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      If tcp_trace Then
                                          raise_error(error_type.warning,
                                                      "failed to connect to ",
                                                      p.identity())
                                      End If
                                      c.shutdown()
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  assert(Not c Is Nothing)
                                  If ec.end_result() Then
                                      If p.insert(c) Then
                                          Return goto_end()
                                      ElseIf tcp_trace Then
                                          raise_error(error_type.warning,
                                                      "failed to insert connection to ",
                                                      p.identity(),
                                                      ", will close the connection")
                                      End If
                                  ElseIf tcp_trace Then
                                      raise_error(error_type.warning,
                                                  "failed to verify token for ",
                                                  p.identity())
                                  End If
                                  c.shutdown()
                                  Return False
                              End Function)
    End Function

    Private Shared Function generate_connection(ByVal p As powerpoint) As event_comb
        assert(Not p Is Nothing)
        assert(p.is_outgoing)
        Dim ec As event_comb = Nothing
        Dim add As pointer(Of IPAddress) = Nothing
        Dim ecs() As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  add = New pointer(Of IPAddress)()
                                  ec = If(p.ipv4,
                                          resolve_ipv4(p.host_or_ip, add),
                                          resolve_ipv6(p.host_or_ip, add))
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() AndAlso
                                     Not (+add) Is Nothing Then
                                      assert(p.connected_count() < p.max_connected)
                                      Dim expected As Int32 = 0
                                      expected = min(p.max_connecting, p.max_connected - p.connected_count())
                                      ReDim ecs(expected - 1)
                                      For i As Int32 = 0 To expected - 1
                                          ecs(i) = generate_connection(+add, p)
                                      Next
                                      Return waitfor(ecs) AndAlso
                                             goto_next()
                                  Else
                                      If tcp_trace Then
                                          raise_error(error_type.warning,
                                                      "failed to resolve host or ip ",
                                                      p.host_or_ip,
                                                      " to ip address for ",
                                                      p.identity())
                                      End If
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ecs.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Public Shared Sub connect(ByVal p As powerpoint)
        assert(Not p Is Nothing)
        assert(p.is_outgoing)
        Dim ec As event_comb = Nothing
        begin_lifetime_event_comb(AddressOf p.expired,
                                  Function() As Boolean
                                      If p.connected_count() < p.max_connected Then
                                          ec = generate_connection(p)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      Else
                                          Return waitfor(constants.interval_ms.connector_check)
                                      End If
                                  End Function,
                                  Function() As Boolean
                                      Return (ec.end_result() OrElse
                                              waitfor(constants.interval_ms.connector_fail)) AndAlso
                                             goto_begin()
                                  End Function)
    End Sub
End Class
